// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2015 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "peel_outer_hull_layers.h"
#include "outer_hull_legacy.h"
#include "../../LinSpaced.h"
#include <vector>
#include <iostream>
//#define IGL_PEEL_OUTER_HULL_LAYERS_DEBUG
#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG
#include "../../writePLY.h"
#include "../../writeDMAT.h"
#include "../../STR.h"
#endif

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedI,
  typename Derivedflip>
IGL_INLINE int igl::copyleft::cgal::peel_outer_hull_layers(
  const Eigen::MatrixBase<DerivedV > & V,
  const Eigen::MatrixBase<DerivedF > & F,
  Eigen::PlainObjectBase<DerivedI> & I,
  Eigen::PlainObjectBase<Derivedflip > & flip)
{
  typedef Eigen::Matrix<typename DerivedF::Scalar ,Eigen::Dynamic,DerivedF::ColsAtCompileTime> MatrixXF;
  typedef Eigen::Matrix<int ,Eigen::Dynamic,1> MatrixXI;
  typedef Eigen::Matrix<typename Derivedflip::Scalar ,Eigen::Dynamic,Derivedflip::ColsAtCompileTime> MatrixXflip;
  const int m = F.rows();
#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG
  std::cout<<"peel outer hull layers..."<<std::endl;
#endif
#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG
  std::cout<<"calling outer hull..."<<std::endl;
  writePLY(STR("peel-outer-hull-input.ply"),V,F);
#endif

#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG
  std::cout<<"resize output ..."<<std::endl;
#endif
  // keep track of iteration parity and whether flipped in hull
  MatrixXF Fr = F;
  I.resize(m,1);
  flip.resize(m,1);
  // Keep track of index map
  MatrixXI IM = igl::LinSpaced<MatrixXI >(m,0,m-1);
  // This is O(n * layers)
  MatrixXI P(m,1);
  int iter = 0;
  while(Fr.size() > 0)
  {
    assert(Fr.rows() == IM.rows());
    // Compute outer hull of current Fr
    MatrixXF Fo;
    MatrixXI Jo;
    MatrixXflip flipr;
#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG
  {
      std::cout<<"calling outer hull..." << iter <<std::endl;
      std::stringstream ss;
      ss << "outer_hull_" << iter << ".ply";
      Eigen::MatrixXd vertices(V.rows(), V.cols());
      std::transform(V.data(), V.data() + V.rows()*V.cols(),
              vertices.data(),
              [](typename DerivedV::Scalar val)
              {return CGAL::to_double(val); });
      writePLY(ss.str(), vertices, Fr);
  }
#endif
    outer_hull_legacy(V,Fr,Fo,Jo,flipr);
#ifdef IGL_PEEL_OUTER_HULL_LAYERS_DEBUG
  writePLY(STR("outer-hull-output-"<<iter<<".ply"),V,Fo);
  std::cout<<"reindex, flip..."<<std::endl;
#endif
    assert(Fo.rows() != 0);
    assert(Fo.rows() == Jo.rows());
    // all faces in Fo of Fr
    std::vector<bool> in_outer(Fr.rows(),false);
    for(int g = 0;g<Jo.rows();g++)
    {
      I(IM(Jo(g))) = iter;
      P(IM(Jo(g))) = iter;
      in_outer[Jo(g)] = true;
      flip(IM(Jo(g))) = flipr(Jo(g));
    }
    // Fr = Fr - Fo
    // update IM
    MatrixXF prev_Fr = Fr;
    MatrixXI prev_IM = IM;
    Fr.resize(prev_Fr.rows() - Fo.rows(),F.cols());
    IM.resize(Fr.rows());
    {
      int g = 0;
      for(int f = 0;f<prev_Fr.rows();f++)
      {
        if(!in_outer[f])
        {
          Fr.row(g) = prev_Fr.row(f);
          IM(g) = prev_IM(f);
          g++;
        }
      }
    }
    iter++;
  }
  return iter;
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
// generated by autoexplicit.sh
template int igl::copyleft::cgal::peel_outer_hull_layers<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::MatrixBase<Eigen::Matrix<CGAL::Epeck::FT, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&);
#endif
